package uk.ac.staffs.student.autopilot.packet;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

public class PacketUtils {

    public static Logger logger = Logger.getLogger(PacketUtils.class);
    /**
     * Packet start identifier
     */
    public static final char PACKET_START = '[';
    /**
     * Packet end identifier
     */
    public static final int gyroPacket = 1;
    
    public static final int accelPacket = 2;
    
    public static final int gpsPacket = 3;
    
    public static final int rtfPacket = 4;
    
    public static final char PACKET_END = ']';

    public PacketUtils() {

    }

    public static int doCheckSum(final int... values) {
        int total = 0;
        for (int i = 0; i < values.length; i++) {
            total += values[i];
        }
        return total;
    }

    /**
	 * 
	 */
    public static PacketInterface buildPacket(final String payload) {
        try {
            final String[] payloadData = payload
                    .split("[\\[\\,\\]\\s+\\r+\\n+]");
            //logger.info("Number of data items split: " + payloadData.length);
            logger.info("Type is: " + payloadData[1]);
            final int pktType = Integer.parseInt(payloadData[1]);
            switch (pktType) {
            case 4:
                logger.info("Building Rtf Packet: " + pktType);
                final int rtfFlag = Integer.parseInt(payloadData[2]);

                final RtfPacket rtfPacket = new RtfPacket(pktType, rtfFlag,
                        rtfFlag);
                logger.info("Data: " + rtfPacket);

                return rtfPacket;

            default:
                logger.error("Invalid packet type or data dropped: " + pktType);
                return null;
            }
        } catch (final ArrayIndexOutOfBoundsException aiobe) {
            logger.info("Missing packet data for packet type: " + payload
                    + " Data: " + payload);
            // Calculate how much of the packet is found and make available for
            // next call.
            // TODO: function
        } catch (final NumberFormatException nfe) {
            // This might not be needed, tests will prove but could be
            // used to tune if too many packets are dropped.
            // For now it does nothing
            logger.error("Dropped packet invalid data, Packet Type: " + payload);
        }

        return null;
    }

    /**
     * Extracts and assembles PacketInterface objects from the data String
     * 
     * @param data
     *            - A String containing the packets, no assumptions is made of
     *            the data in the string. This utility method only returns data
     *            from packets with the correct syntax.
     * 
     *            Java RegExp is used to strip out packets
     * 
     * @return
     */
    public static PacketInterface[] extractPackets(final byte[] data) {

        String strData = new String(data);
        //logger.info("data buffer size: " + data.length);
        strData = strData.replaceAll("\\s+", "");
        final List<PacketInterface> packets = new ArrayList<PacketInterface>();

        // Regular expression to strip out all valid markers and
        // leave only the data for validation.
        // first value will be the packet identifyer
        final String[] splitData = strData.split("[\\[\\,\\]\\s+\\r+\\n+]");
        logger.info("No of Strings: " + splitData.length);
        String string = "";
        for (int i = 0; i < splitData.length; i++) {
            string = splitData[i];
            if (string.length() > 0 && string != null) {

                try {
                    final int pktType = Integer.parseInt(string);
                    logger.info("Data type: " + pktType);
                    switch (pktType) {
                    case PacketUtils.gyroPacket:
                        logger.info("Gyro packet type: " + splitData[i]);

                        final int gyroPktTypeInt = pktType;
                        final int axInt = Integer.parseInt(splitData[++i]);
                        final int ayInt = Integer.parseInt(splitData[++i]);
                        final int azInt = Integer.parseInt(splitData[++i]);
                        final int checkSumInt = Integer
                                .parseInt(splitData[++i]);

                        final TelemetryGyrosPacket packetGyros = new TelemetryGyrosPacket(
                                gyroPktTypeInt, axInt, ayInt, azInt,
                                checkSumInt);
                        logger.info("Data: " + packetGyros);
                        packets.add(packetGyros);
                        // Try to create a packet using the data that should
                        // follow.

                        break;

                    case PacketUtils.accelPacket:
                        logger.info("Accels packet type: " + splitData[i]);

                        final int accelPktTypeInt = pktType;
                        final int acxInt = Integer.parseInt(splitData[++i]);
                        final int acyInt = Integer.parseInt(splitData[++i]);
                        final int aczInt = Integer.parseInt(splitData[++i]);
                        final int accheckSumInt = Integer
                                .parseInt(splitData[++i]);
                        final TelemetryAccelsPacket packetAccels = new TelemetryAccelsPacket(
                                accelPktTypeInt, acxInt, acyInt, aczInt,
                                accheckSumInt);
                        logger.info("Data: " + packetAccels);
                        packets.add(packetAccels);
                        logger.info("Packet added: " + packetAccels);
                        break;
                    case PacketUtils.gpsPacket:
                        logger.info("GPS packet type: " + splitData[i]);
                        logger.info("Number of tokens: " + splitData.length);
                        final int gpsPktTypeInt = pktType;
                        final long latitude = Long
                                .parseLong(splitData[++i]);
                        final long longitude = Long
                                .parseLong(splitData[++i]);
                        final long altitude = Long
                                .parseLong(splitData[++i]);
                        final long groundSpeed = Long
                                .parseLong(splitData[++i]);
                        final long groundCourse = Long
                                .parseLong(splitData[++i]);
                        final int numSats = Integer.parseInt(splitData[++i]);
                        final int fix = Integer.parseInt(splitData[++i]);
                        final long time = Long.parseLong(splitData[++i]);
                        final int checkSum = Integer.parseInt(splitData[++i]);
                        logger.info("before packet object is constructed");
                        final GpsPacket packetGps = new GpsPacket(gpsPktTypeInt,
                                latitude, longitude, altitude, groundSpeed,
                                groundCourse, numSats, fix, time, checkSum);
                        logger.info("Data: " + packetGps);
                        packets.add(packetGps);
                        break;

                    case PacketUtils.rtfPacket:
                        logger.info("Ready to Fly packet type: " + pktType);
                        final int ack = Integer.parseInt(splitData[++i]);

                        final RtfPacket packetRtf = new RtfPacket(4, ack, ack);
                        logger.info("Data: " + packetRtf);
                        packets.add(packetRtf);

                    default:
                        logger.error("Invalid packet type or data dropped: "
                                + pktType);
                        break;
                    }
                } catch (final ArrayIndexOutOfBoundsException aiobe) {
                    logger.info("Missing packet data for packet type: "
                            + string + " Data: " + splitData[i - 1] + " "
                            + splitData[i - 2]);
                    // Calculate how much of the packet is found and make
                    // available for next call.
                    // TODO: function
                } catch (final NumberFormatException nfe) {
                    // This might not be needed, tests will prove but could be
                    // used to tune if too many packets are dropped.
                    // For now it does nothing
                    checkStartNoEnd(string);
                    logger.error("Dropped packet invalid data, Packet Type: "
                            + string);
                } catch (final NullPointerException e) {
                    logger.error("Exception thrown " + e);
                }

            }
        }
        //logger.info("Before return statement");
        return packets.toArray(new PacketInterface[packets.size()]); // maybe
                                                                     // here?

    }

    private static void checkStartNoEnd(final String string) {
        // TODO Auto-generated method stub

    }

    @SuppressWarnings("unused")
	private static void endOfPacket(final String substring) {
        // TODO Auto-generated method stub
        logger.info("endOfPacket received: " + substring);

    }

}
